////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_FUNCTION_MANAGER_H__
#define __WN_FUNCTION_MANAGER_H__

#include "WNScripting/inc/WNIdentifierMap.h"
#include "WNScripting/inc/WNTypeManager.h"
#include "WNScripting/inc/WNScriptingEnginePublic.h"
#include "WNScripting/inc/WNFunction.h"

#include <iostream>

#define DEFINE_FUNCTION(mgr, returntype, _func, ...)  mgr->AddExternalFunction(ptr, returntype, #_func, ##__VA_ARGS__)

struct WNScriptingEngineData;
class WNScriptingEngine;

static WN_VOID* StaticGenerationFunction(WNScriptingEngine* _engine, WNFunction* _function) {

    if (_engine && _function) {
        _engine->CompileFunction(_function);
    }

    return(_function->mFunctionPointer);
}

class WNFunctionManager {
public:
    enum ReturnCode {
        #include "WNCore/inc/WNErrors.h"
        WN_FUNCTION_MANAGER_DOES_NOT_EXIST,
        WN_FUNCTION_MANAGER_INVALID_IDENTIFIER,
        WN_FUNCTION_MANAGER_INVALID_CONVENTION,
        WN_FUNCTION_MANAGER_CONFLICTING_DEFINITION,
        WN_FUNCTION_MANAGER_IDENTICAL_DUPLICATES
    };

    WNFunctionManager(WNScriptingEngineData& _scriptingData);
    WN_VOID AddNewThread();

    WN_VOID CreateInternalFunction(WNFunction*& _funcLocation, const WN_CHAR* _functionName, WN_VOID* _funcPtr, llvm::Type* _ptrType, std::vector<llvm::Type*> _llvmTypes);

    ~WNFunctionManager();
    ReturnCode AddExternalFunction(WN_VOID* _funcPtr, WNIdentifierElement _name, WNTypeElement _returnType, std::vector<WNTypeElement>& _params);
    ReturnCode AddExternalFunction(WN_VOID* _funcPtr, WN_BOOL _stack, const WN_CHAR* _returnType, const WN_CHAR* _name, const WN_CHAR* _arglist);
    ReturnCode AddIncompleteScriptFunction(WNIdentifierElement _name, WNIdentifierElement _file, WN_BOOL _onStack, WNTypeElement _returnType, const std::vector<WNTypeElement>& _params, WN_SIZE_T _offset, WN_SIZE_T _endOffset);
    WN_VOID GetScriptFunctions(std::vector<WNFunction*>& _functions, WNIdentifierElement _name, WNIdentifierElement _file);
    WN_VOID GetExternalFunctions(WNIdentifierElement _name, std::vector<WNFunction*>& _functions, WN_BOOL _eTypes);
    ReturnCode GetExactScriptFunction(WNFunction*& _function, WNIdentifierElement _name, const std::vector<WNTypeElement>& _params, WNIdentifierElement _file);
    
    WNIdentifierMap* GetIdentifierMap();
    WNFunction* GetCreateFunctionFunction();
    WNFunction* GetMallocFunction();
    WNFunction* GetFreeFunction();
    WNFunction* GetMemcpyFunction();
    WNFunction* GetTemporaryStackFunction();
    WN_VOID SetMallocFunction(WNScriptAllocFunc _scriptFunc);
    WN_VOID SetFreeFunction(WNScriptFreeFunc _freeFunc);
    WN_VOID SetAlignedMallocFunction(WNScriptAlignedMallocFunc _alignedFunc);
    WN_VOID SetAlignedFreeFunction(WNScriptAlignedFree _alignedFunc);
    WN_VOID SetMemcpyFunction(WNScriptMemcpy _memcpy);

    WN_VOID* CheckSystemFunctions(const std::string& _function);
    WN_VOID AddIncludeFile(WNIdentifierElement _file, WNIdentifierElement _includedFile);
private:
    typedef WN_UNORDERED_MULTIMAP<WNIdentifierElement, WNFunction*> FunctionMMap;
    typedef WN_UNORDERED_MULTIMAP<WNIdentifierElement, WNIdentifierElement> IncludeMMap;

    WN_BOOL ContainsFile(IncludeMMap::iterator& _start, IncludeMMap::iterator& _end, WNIdentifierElement _element);
    WN_VOID GenerateLLVMFunction(WNFunction*, WN_BOOL);
    WNScriptingEngineData& mScriptingData;
    FunctionMMap mFunctionMap;
    FunctionMMap mNativeMap;
    IncludeMMap mIncludeMap;
    WNFunction* mCreateFunctionfunction;
    WNFunction* mMallocFunction;
    WNFunction* mFreeFunction;
    WNFunction* mAlignedFreeFunction;
    WNFunction* mAlignedMallocFunction;
    WNFunction* mMemcpyFunction;
    WNFunction* mInternalTempStackFunction;

    WNScriptAllocFunc mInternalMallocFunc;
    WNScriptFreeFunc  mInternalFreeFunc;
    WNScriptAlignedMallocFunc mInternalAlignedMalloc;
    WNScriptAlignedFree mInternalAlignedFree;
    WNScriptMemcpy   mInternalMemcpy;
};

#endif // __WN_FUNCTION_MANAGER_H__
